昨天的文章提到 keyof
型別運算子可以用來取得物件鍵值(key)型別,並能將鍵值型別們union成一個型別;若單純從取得的型別來看,現在要認識的indexed accessed types 的作用和 keyof
正好相反。
Indexed accessed types是「透過 "索引" 鍵值來取得鍵值的型別_」,甚至能"索引"多個鍵值的型別並union成一個型別。
這裡的索引是動詞,可想成是「查找」,但光看繞口的說明可能會有點霧煞煞,直接來看一些例子。
這個例子是要取得物件型別的鍵值型別:
interface Dog {
name: string;
age: number;
hasOwner: boolean;
}
type D1 = Dog["name"]; // type D1 = string
type D2 = Dog["age" | "hasOwner"]; // type D2 = number | boolean
// Get types of all keys
type D3 = Dog["name" | "age" | "hasOwner"]; // type D3 = string | number | boolean
// or
type DogKeys = "name" | "age" | "hasOwner";
type D3Keystypes = Dog[Dogkeys]; // type D3Keystypes = string | number | boolean
// or
type D3KeyofTypes = Dog[keyof Dog]; // type D3KeyofTypes = string | number | boolean
那如果Dog鍵值的型別有重複會怎麼樣?
interface Dog {
name: string;
age: number;
hasOwner: boolean;
h: number;
w: number;
}
type D = Dog[keyof Dog]; // type D = string | number | boolean
結果不是 string | number | boolean | number | number
,因為union不必重複型別。
union不必重複型別的原因在於,union型別的意思是:
(變數) 可以是A型別或是B型別
如上面例子的型別D代表 可以是 string 或 number 或 boolean型別。
若能union重複型別,那 string | number | boolean | number | number
型別的意思會變成:
(變數) 可以是 string 或 number 或 boolean 或 number 或 number 型別
前面已經允許 可以是number型別 ,後面就不用再允許兩次可以是number型別了吧XD。
此外,若想取得陣列元素的型別,能用 typeof
型別運算子和 number
關鍵字去索引陣列元素來取得型別:
const dogs = [
{
name: "Harry",
age: 2,
hasOwner: true
},
{
name: "Black",
age: 5,
hasOwner: false
},
];
type DogName = typeof dogs[number]["name"]; // type DogName = string;
type Dog = typeof dogs[number];
/*
type Dog = {
name: string;
age: number;
hasOwner: boolean;
}
*/
在用索引方式取得型別時,有一點要注意:用來當作索引的是型別,不可是變數值(value)。
前面例子的 "name"
或 "name" | "age" | "hasOwner"
其實都是literal型別,並非是變數值或屬性值。
如果用變數值取得型別,compiler會顯示多個錯誤:
let age = "age";
const hasOwner = "hasOwner";
type DogAge = Dog[age]; // error
type DogHasOwner = Dog[hasOwner]; // error
其中可以看到最重要的錯誤是:
'age' refers to a value, but is being used as a type here. Did you mean 'typeof age'?(2749)
除非變數是literal型別,並同時使用 typeof
型別運算子去取得變數literal型別,否則變數值是沒辦法索引型別的,就像下面例子中的 let age
:
// 1.
let age = "age";
const hasOwner = "hasOwner";
type DogAge = Dog[typeof age]; // error
type DogHasOwner = Dog[typeof hasOwner] // ok, hasOwner is "hasOwner" type
// 2.
let ageType: "age" = "age";
const hasOwnerType: "hasOwner" = "hasOwner";
type DogAgeType = Dog[typeof ageType]; // ok
type DogHasOwnerType = Dog[typeof hasOwnerType]; // ok
這邊有個線上範例可以來玩玩看單純用 typeof age
和 typeof ageType
去取得indexed accessed types的差別。
參考資料
Indexed Access Types @TypeScript Handbook